/************************************************************************************
 * Nombre del archivo: Shell.java
 * Universidad del Valle de Guatemala
 * Curso: Sistemas Operativos
 * @author martin
 *          Martín Guzmán 08041
 * Fecha: Febrero de 2010
 * Descripción: Contiene el shell del proyecto. En la línea de comando
 *              se mandan a llamar a los system programs y a los user
 *              programs
*************************************************************************************/

import java.io.File;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.BufferedReader;
import java.io.File;
import java.io.*;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.LinkedList;


public class Shell extends Thread{

    private Scanner scan = new Scanner(System.in);
    public volatile static File Directorio_Raiz = new File("/home/martin/Desktop/Sistemas/ejemplos");

    public volatile static String path_io_log = "/home/martin/Desktop/Sistemas_operativos/logs/io.log"; //roots de los logs
    public volatile static String path_ps_log = "/home/martin/Desktop/Sistemas_operativos/logs/ps.log"; //roots de los logs
    public volatile static String path_mem_log = "/home/martin/Desktop/Sistemas_operativos/logs/mem.log"; //roots de los logs

    private static LinkedList<String> lista_instrucciones = new LinkedList<String>();
    private static boolean sincrono=true;
    private static File outputFile;
    private static FileWriter out;

    public static boolean debug_mode=false;

    public static boolean sincrono_debug=true;
    public static String debug_path="";
    private static File outputFile_debug;
    private static FileWriter out_debug;

    private static int root = 0;

    public static HashMap Conjunto_SystemPrograms = new LinkedHashMap();

    public Shell(){
        System.out.println("   _________               ______                   ___     ______               _____       ____   \n"
                            +"   |''''[]'|=======[]    .' ___  |                .'   `. .' ____ \\             / ___ `.   .'    '.    \n"
                            +"   |________\\____       / .'   \\_| __   _   ____ /  .-.  \\| (___ \\_|  _   __   |_/___) |  |  .--.  |  \n"
                            +"   |==+==========\\      | |   ____[  | | | [_   ]| |   | | _.____`.  [ \\ [  ]   .'____.'  | |    | |   \n"
                            +"   |______________|     \\ `.___]  || \\_/ |, .' /_\\  `-'  /| \\____) |  \\ \\/ /_  / /_____  _|  `--'  |  \n"
                            +"   \\(@))(@)(@)(@)/       `._____.' '.__.'_/[_____]`.___.'  \\______.'   \\__/(_) |_______|(_)'.____.' \n"
                            +"##########################################################################################################");


        System.out.println("\nWelcome to GuzOS v.0.1 <http://code.google.com/p/guz08041/>");
        System.out.println("Type help and hit Enter for context help. Or type exit to quit.\n");

        Conjunto_SystemPrograms.put("ls",new Integer(1));
        Conjunto_SystemPrograms.put("ps",new Integer(2));
        Conjunto_SystemPrograms.put("kill",new Integer(3));
        Conjunto_SystemPrograms.put("top",new Integer(4));
        Conjunto_SystemPrograms.put("rm",new Integer(5));
        Conjunto_SystemPrograms.put("cd",new Integer(6));
        Conjunto_SystemPrograms.put("cp",new Integer(7));
        Conjunto_SystemPrograms.put("mkdir",new Integer(8));
        Conjunto_SystemPrograms.put("mv",new Integer(9));
        Conjunto_SystemPrograms.put("du",new Integer(10));
        Conjunto_SystemPrograms.put("rename",new Integer(11));
        Conjunto_SystemPrograms.put("print",new Integer(12));

        //System_program system_program = new System_program();
    }

    /***********************************************
     * Método: run
     * Uso: Método que ejecuta el shell mientras está
     *      encendido
     *@return void
     ***********************************************/
    public void run() {

        String instruccion_actual="";
        do{
            //se queda en loopado hasta que se acabe el proceso síncrono... ****
            try{
                //TODO arreglar esto...
                while(CPU.contiene_proceso_sincrono()){
                    try {

                        this.sleep(10);
                    } catch (Exception ex) {
                        System.out.println("Exception en sleep");
                    }
                }
            }catch(Exception exp){}
            //******************************************************************

            instruccion_actual = Prompt();
            String[] inst = instruccion_actual.split("-");

            for(int i=0;i<inst.length;i++){
                instruccion_actual = this.Quitar_espacios(inst[i]);
                //System.out.println(" ---------- instrucción -----------------"+instruccion_actual+".");
                //while(CPU.contiene_proceso_sincrono()){}

                //revisa la salida del programa
                if(instruccion_actual.compareToIgnoreCase("exit")==0){
                    break;
                }
                /*
                if(instruccion_actual.compareToIgnoreCase("restart")==0){
                    Main.restart=true;
                    break;
                }
                 */
                if(instruccion_actual.compareToIgnoreCase("help")==0){
                    Help();
                }
                else if(revisar_system_program(instruccion_actual)){}
                else if(se_encuentra_en_directorio_raiz_y_es_archivo(instruccion_actual)){
                    Crear_Proceso(instruccion_actual);
                }
                else{
                    if(instruccion_actual!="")
                        System.out.println(instruccion_actual+": command not found");
                }
                //*******************************************************

            }
            if(instruccion_actual.compareToIgnoreCase("exit")==0){
                break;
            }
            if(instruccion_actual.compareToIgnoreCase("restart")==0){
                break;
            }
        }while(true);
    }

    /***********************************************
     * Método: Prompt
     * Uso: Despliega el prompt para que ingrese
     * las instrucciones a ejecutar.
     *@return instrucción ingresada por el usuario.
     ***********************************************/
    public String Prompt(){
        //System.out.print(this.Directorio_Raiz.getPath()+": ");
        Print(CPU.fileSystem.getPath(Shell.root)+"$ ");
        return Quitar_espacios(scan.nextLine());
    }

    /***********************************************
     * Método: comparar_cadenas
     * Uso: compara 2 cadenas
     * @param cadena1 a comparar
     * @param cadena2 para comparar con cadena1
     * @return booleano que dice si son iguales las cadenas
    ***********************************************/
    private static boolean comparar_cadenas(String cadena1, String cadena2) {

        if(cadena1.compareTo(cadena2)==0){
            return true;
        }
        return false;
    }

    /***********************************************
     * Método: Quitar espacios
     * Uso: quita espacios inservibles
     * @return cadena sin espacios inservibles
     *          y separando los átomos por
     *         espacios
    ***********************************************/
    public String Quitar_espacios(String instruccion){
        String retorno=instruccion;

        while(instruccion.contains("\t"))
            retorno=retorno.replace("\t"," ");

        while(retorno.contains("  ")){
            retorno=retorno.replaceAll("  "," ");
        }

        //por si inicia con un espacio
        if(retorno.startsWith(" ")){
            retorno = retorno.substring(1);
        }

        //quitar el espacio al final
        if(retorno.endsWith(" ")){
            retorno = retorno.substring(0, retorno.length()-1);
        }

        if(retorno.length()==0){
            return "";
        }

        return retorno;
    }

    /*****************************************
     * Método: setDirectorio_Raiz
     * Uso: cambia el directorio raiz
     * @param path un arhivo que contiene el path que se desea para directorio raiz
     * @return instrucciones para usos.
     *****************************************/
    public static void setDirectorio_Raiz(String path){
        Directorio_Raiz = new File(path);
    }

    /*****************************************
     * Método: Help
     * Uso: Ayuda para el usuario.
     * @return instrucciones para usos.
     *****************************************/
    public void Help(){
        System.out.print("helping..!");//TODO arreglar la ayuda
    }


    /***********************************************
     * Método: revisar_system_program
     * Uso: revisa si hay alguna acción que tenga
     *      que hacer el system program
     * @param cadena que contiene la instrucción a revisar
     * @return booleano que dice si se realizó una acción
    ***********************************************/
    public static boolean revisar_system_program(String cadena){
        
        boolean retorno = false;
        
        String a_revisar = cadena.split(" ")[0];
        String[] arreglo = cadena.split(" ");
        int comando_a_ejecutar = -1;
        if(Shell.Conjunto_SystemPrograms.containsKey(a_revisar.toLowerCase())){
            comando_a_ejecutar = (Integer)Shell.Conjunto_SystemPrograms.get(a_revisar.toLowerCase());
        }
        switch(comando_a_ejecutar){
            //------------------------------------------------------------------
            //          ls
            //------------------------------------------------------------------
            case 1:
                //Println("Shell.revisar_system_program: ls");
                if(arreglo.length!=1)
                    break;
                ls();
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          ps
            //------------------------------------------------------------------
            case 2:
                Println("Shell.revisar_system_program: ps");
                if(arreglo.length!=1)
                    break;
                ps();
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          kill
            //------------------------------------------------------------------
            case 3:
                Println("Shell.revisar_system_program: kill");
                if(arreglo.length!=2)
                    break;
                kill(arreglo[1]);
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          top
            //------------------------------------------------------------------
            case 4:
                Println("Shell.revisar_system_program: top");
                if(arreglo.length!=1)
                    break;
                top();
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          rm
            //------------------------------------------------------------------
            case 5:
                //Println("Shell.revisar_system_program: rm");
                if(arreglo.length!=2)
                    break;
                rm(arreglo[1]);
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          cd
            //------------------------------------------------------------------
            case 6:
                //Println("Shell.revisar_system_program: cd");
                if(arreglo.length!=2)
                    break;
                cd(arreglo[1]);
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          cp
            //------------------------------------------------------------------
            case 7:
                //Println("Shell.revisar_system_program: cp");
                if(arreglo.length!=3)
                    break;
                cp(arreglo[1],arreglo[2]);
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          mkdir
            //------------------------------------------------------------------
            case 8:
                //Println("Shell.revisar_system_program: mkdir");
                if(arreglo.length!=2)
                    break;
                mkdir(arreglo[1]);
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          mv
            //------------------------------------------------------------------
            case 9:
                Println("Shell.revisar_system_program: mv");
                if(arreglo.length!=3)
                    break;
                mv(arreglo[1],arreglo[2]);
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          du
            //------------------------------------------------------------------
            case 10:
                //Println("Shell.revisar_system_program: du");
                if(arreglo.length!=1)
                    break;
                du();
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          rename
            //------------------------------------------------------------------
            case 11:
                //Println("Shell.revisar_system_program: rename");
                if(arreglo.length!=3)
                    break;
                rename(arreglo[1],arreglo[2]);
                retorno = true;
                break;
            //------------------------------------------------------------------
            //          print
            //------------------------------------------------------------------
            case 12:
                //Println("Shell.revisar_system_program: print");
                if(arreglo.length!=2)
                    break;
                Print_file(arreglo[1]);
                retorno = true;
                break;
        }
        return retorno;
    }

    /***********************************************
     * Método: se_encuentra_en_directorio_raiz_y_es_archivo
     * Uso: revisa si se quiere ejecutar un user program
     * @param cadena que contiene la instrucción a revisar
     * @return booleano que dice si lo pedido es un user program
    ***********************************************/
    public static boolean se_encuentra_en_directorio_raiz_y_es_archivo(String cadena){

        String cadena1 = cadena.split(" ")[0];
        boolean retorno = CPU.fileSystem.existNameKid(Shell.getRoot(), cadena1);
        if(retorno== true){
            return CPU.fileSystem.getDirectorio(CPU.fileSystem.getIndiceHijo(Shell.root, cadena1)).isArchivo();
        }
        return false;
        /*
        File[] file2 = Directorio_Raiz.listFiles();

        //ciclo lo tecleado está en el directorio raiz
        for(int i=0;i<file2.length;i++){
            //revisa si los nombres son iguales y si el file que se está revisando no es carpeta
            if(comparar_cadenas(file2[i].getName(),cadena1) && file2[i].isFile()){
                return true;
            }
        }
        return false;
         */
    }

    /***********************************************
     * Método: ls
     * Uso: imprime en pantalla todos los archivos del
     *      directorio raiz
     * @return void imprime directo en pantalla
     *      los archivos del directorio raiz
    ***********************************************/
    private static void ls() {
        int indice_a_buscar = Shell.root;
        Println("");
        String format = "| %1$-32s | %2$-13s | %3$-7s | %4$-28s | %5$-28s |\n";
        System.out.format(format, "            Nombre", "   Tamaño","Carpeta", "      Fecha creación", "    Fecha modificación");
        System.out.format(format, " ----------------------------- ", " ----------- "," ----- ", " -------------------------- ", " -------------------------- ");
        //----------------------------------------------------------------------
        LinkedList<Integer> lista_hijos = CPU.fileSystem.getAllKids(indice_a_buscar);
        for(int i=0;i<lista_hijos.size();i++){
            
            FCB fcb = CPU.fileSystem.getDirectorio(lista_hijos.get(i));
            if(fcb != null){
                String nombre = fcb.getNombre();
                String fecha_creacion = fcb.getFecha_creacion_formato();
                String fecha_modificacion = fcb.getFecha_modificacion_formato();
                String tamano = CPU.fileSystem.getTamanoFile(lista_hijos.get(i))+" bytes";
                Boolean carpeta = fcb.isCarpeta();
                System.out.format(format, nombre, tamano, carpeta.toString(),fecha_creacion,fecha_modificacion);
            }
        }
        //----------------------------------------------------------------------
        Println("");
    }

    /***********************************************
     * Método: cd
     * Uso: se mete a la carpeta o retrocede de carpeta
     * @return void
    ***********************************************/
    private static void cd(String cadena) {
        //Shell.root;
        //path relativo
            //verificar que sea hijo de la carpeta actual y que sea carpeta
        //path absoluto -> empieza con "~/"
            //verificar que el path exista y que todos los "/" sean para carpetas
        String[] arreglo = cadena.split("/");

        if(arreglo.length==1){
            //Println("Shell.cd: Path relativo");
            //path relativo
            if(arreglo[0].compareTo("~")==0){
                Shell.root = 0;
            }
            else if(CPU.fileSystem.isHijo(Shell.root, arreglo[0]) == true){
                int indice = CPU.fileSystem.getIndiceHijo(Shell.root, arreglo[0]);
                if(CPU.fileSystem.getDirectorio(indice).isCarpeta() == true){
                    Shell.root = indice;
                }
                else{
                    Println("Shell.cd: No se puede acceder al archivo ya que no es una carpeta");
                }
            }
            else if(arreglo[0].compareTo("..")==0){
                //regresar una carpeta
                int indice = CPU.fileSystem.getDirectorio(Shell.root).getId_padre_value();
                if(indice != -1){
                    Shell.root = indice;
                }
            }
            else{
                Println("Shell.cd: Path relativo inexistente");
            }
        }
        else{
            //Println("Shell.cd: Path absoluto");
            int inicio = -1;
            if(arreglo[0].compareTo("~")==0){
                inicio = 0;
            }
            else if(arreglo[0].compareTo("..")==0){
                    //regresar una carpeta
                int indice = CPU.fileSystem.getDirectorio(Shell.root).getId_padre_value();
                if(indice != -1){
                    inicio = indice;
                }
                else
                    inicio = Shell.root;
            }
            else if(CPU.fileSystem.existNameKid(Shell.root, arreglo[0]) == true){
                inicio = CPU.fileSystem.getIndiceHijo(Shell.root, arreglo[0]);
            }
            else{
                inicio = Shell.root;
            }
            boolean error = false;
            for(int i=1;i<arreglo.length;i++){
                Println("Shell.cd: i: "+i+" arreglo: ."+arreglo[i]+".");
                if(CPU.fileSystem.existNameKid(inicio, arreglo[i]) == true){
                    inicio = CPU.fileSystem.getIndiceHijo(inicio, arreglo[i]);
                }
                else if(arreglo[i].compareTo("..")==0){
                    //regresar una carpeta
                    int indice = CPU.fileSystem.getDirectorio(inicio).getId_padre_value();
                    if(indice != -1){
                        inicio = indice;
                    }
                }
                else
                    if(i!=arreglo.length-1){
                    error = true;
                    break;
                }
            }
            if(error == false){
                Shell.root = inicio;
            }
            else{
                Println("Shell.cd: No existe el path ingresado");
            }
            //path absoluto

        }


    }

    /***********************************************
     * Método: rename
     * Uso: cambia el nombre de un archivo o carpeta hijo
     * @return void
    ***********************************************/
    private static void rename(String nombre, String nuevo_nombre) {
        String letter="[a-zA-Z]";
        String digit="\\d";
        String ident=letter+"("+letter+"|"+digit+"|\\.)*";
        //revisión de que el nombre sea correcto
        if(nuevo_nombre.matches(ident) == false || nuevo_nombre.length()>=32){
            Println("No se puede cambiar por el nombre: "+nuevo_nombre+" utilice sólo letras, números y '.' y revise el tamaño");
        }

        int indice = CPU.fileSystem.getIndiceHijo(Shell.root, nombre);
        if(indice==-1){
            Println("No existe el archivo que se desea renombrar: "+nombre);
        }
        else{
            if(CPU.fileSystem.existNameKid(Shell.root, nuevo_nombre) == true){
                Println("Ya existe un archivo con ese nombre: "+nuevo_nombre);
            }
            else{
                CPU.fileSystem.getDirectorio(indice).setNombre(nuevo_nombre);
                CPU.fileSystem.getDirectorio(indice).setFecha_modificacion();
            }
        }

    }

    /***********************************************
     * Método: Print_file
     * Uso: Imprime todo el contenido de un archivo
     * @return void
    ***********************************************/
    private static void Print_file(String nombre) {
        Println("");
        if(CPU.fileSystem.existNameKid(Shell.root, nombre)){
            int a = CPU.fileSystem.getIndiceHijo(Shell.root, nombre);
            if(CPU.fileSystem.getDirectorio(a).isArchivo()){
                int b = CPU.fileSystem.getDirectorio(a).getPrimer_bloque_de_datos_value();
                int c = CPU.fileSystem.getDirectorio(a).getTamano_real_value();
                Println(CPU.fileSystem.getAllDatos_string(b,c));
            }
            else{
                CPU.fileSystem.print_folder(a);
            }
        }
        else if(nombre.compareTo("~")==0){
            CPU.fileSystem.print_folder(0);
        }
        Println("");
    }

    /***********************************************
     * Método: cp
     * Uso: se mete a la carpeta o retrocede de carpeta
     * @return void
    ***********************************************/
    private static void cp(String fuente, String destino) {
        //"~/...." --> del SO proyecto
        //"@/...." --> del SO host
        //path relativo
            //verificar que sea hijo de la carpeta actual y que sea carpeta
        //path absoluto -> empieza con "~/"
            //verificar que el path exista y que todos los "/" sean para carpetas
        boolean fuente_host = (fuente.split("/")[0].compareTo("@")==0);
        boolean destino_host = (destino.split("/")[0].compareTo("@")==0);

        if(fuente_host && destino_host==false){
            //de host a acá
            String fuente_1 ="";
            try{
                fuente_1 = fuente.substring(1);
            }catch(Exception exp){
                fuente_1 = fuente;
            }
            CPU.fileSystem.cp_host_this(fuente_1, destino);
        }
        else if(fuente_host==false && destino_host){
            //de acá al host
            String destino_1 ="";
            try{
                destino_1 = destino.substring(1);
            }catch(Exception exp){
                destino_1 = fuente;
            }
            if(destino_1.endsWith("/")==false){
                destino_1+="/";
            }
            CPU.fileSystem.cp_this_host(fuente, destino_1);
        }
        else if(fuente_host==false && destino_host==false){
            //de acá a acá
            CPU.fileSystem.cp_this_this(fuente, destino);
        }
        else{
            Println("Shell.cp: No se pueden copiar archivos del sistema operativo host a él mismo..");
        }
    }

    /***********************************************
     * Método: rm
     * Uso: borra un archivo o carpeta del so
     * @return void
    ***********************************************/
     private static void rm(String cadena) {
        Println("");
        int inicio_1 = -1;
        if(cadena.split("/")[0].compareTo("~")==0){
            inicio_1 = 0;
        }else{
            inicio_1 = Shell.getRoot();
        }
        if(CPU.fileSystem.existPath(cadena, inicio_1) == false){
            Println("Shell.rm: No existe el archivo: "+cadena+", por lo que no se puede remover");
        }
        else{
            int id_fuente = CPU.fileSystem.getPath_id(cadena, inicio_1);
            //Println("identificador: "+id_fuente);
            CPU.fileSystem.rm(id_fuente, true);
        }

        /*
         if(CPU.fileSystem.existNameKid(Shell.root, cadena) == true){
             int indice = CPU.fileSystem.getIndiceHijo(Shell.root, cadena);
             //TODO terminar el rm
             CPU.fileSystem.rm(indice, true);
         }
         else{
             Print("No existe el archivo, por lo tanto no se puede remover");
         }
         */
        Println("");
    }

    /***********************************************
     * Método: mv
     * Uso: mueve un archivo de carpeta
     * @return void
    ***********************************************/
    private static void mv(String fuente, String destino) {
        //"~/...." --> del SO proyecto
        //"@/...." --> del SO host
        boolean fuente_host = (fuente.split("/")[0].compareTo("@")==0);
        boolean destino_host = (destino.split("/")[0].compareTo("@")==0);

        if(fuente_host && destino_host==false){
            //de host a acá
            String fuente_1 ="";
            try{
                fuente_1 = fuente.substring(1);
            }catch(Exception exp){
                fuente_1 = fuente;
            }
            CPU.fileSystem.mv_host_this(fuente_1, destino);
        }
        else if(fuente_host==false && destino_host){
            //de acá al host
            String destino_1 ="";
            try{
                destino_1 = destino.substring(1);
            }catch(Exception exp){
                destino_1 = fuente;
            }
            if(destino_1.endsWith("/")==false){
                destino_1+="/";
            }
            CPU.fileSystem.mv_this_host(fuente, destino_1);
        }
        else if(fuente_host==false && destino_host==false){
            //de acá a acá
            CPU.fileSystem.mv_this_this(fuente, destino);
        }
        else{
            Println("Shell.mv: No se pueden mover archivos del sistema operativo host a él mismo..");
        }
    }

    /***********************************************
     * Método: mkdir
     * Uso: crea una carpeta en la carpeta donde se encuentre
     * @return void
    ***********************************************/
    private static void mkdir(String nombre) {
        String letter="[a-zA-Z]";
        String digit="\\d";
        String ident=letter+"("+letter+"|"+digit+"|\\.)*";
        //revisión de que el nombre sea correcto
        if(nombre.matches(ident) == false || nombre.length()>=32){
            Println("No se puede crear una carpeta con el nombre: "+nombre+" utilice sólo letras, números y '.' y revise el tamaño");
        }
        else{
            LinkedList<Integer> hijos = CPU.fileSystem.getAllKids(Shell.root);
            boolean hermano = false;
            //revisar si no tiene hijos..
            FCB asignar = null;
            if(hijos.isEmpty()==true){
                asignar = CPU.fileSystem.getDirectorio(Shell.root);
                hermano = false;
            }
            else{
                hermano = true;
                asignar = CPU.fileSystem.getDirectorio(hijos.getLast());

                //revisar si algún hijo ya tiene ese nombre
                for(int i=0;i<hijos.size();i++){
                    if(comparar_cadenas(CPU.fileSystem.getDirectorio(hijos.get(i)).getNombre(),nombre)){
                        //el nombre es igual
                        Println("No se puede crear la carpeta ya que existe otro archivo con el mismo nombre");
                        asignar = null;
                        break;
                    }
                }
            }
            if(asignar != null){
                //entonces es el primer hijo de esta carpeta y hay que crearlo
                int primer_vacio = CPU.fileSystem.getFirstDirectorioEmpty();
                //Println("Shell.mkdir: primer índice vacío de FCB: "+primer_vacio);
                if(primer_vacio == -1){
                    //ya no hay espacio en el file system (ya no hay vacíos)
                    Println("No se puede crear una carpeta ya que ya no hay espacio en el Directorio.");
                }
                else{
                    FCB fcb_creado = CPU.fileSystem.getDirectorio(primer_vacio);
                    boolean correcto = false;
                    if(hermano){
                        //asignar como hermano
                        correcto = asignar.setId_hermano(primer_vacio+"");
                    }
                    else{
                        //asignar como hijo
                        correcto = asignar.setPrimer_bloque_de_datos(""+primer_vacio);
                    }
                    if(correcto==false){
                        Println("Shell.mkdir: No se pudo asignar el identificador");
                    }
                    else{
                        fcb_creado.createFolder(nombre);
                        fcb_creado.setId_padre(Shell.root);
                        fcb_creado.setId(primer_vacio);
                        //CPU.fileSystem.flushFS();
                    }
                }
            }
        }
    }

    /***********************************************
     * Método: du
     * Uso: Muestra el porcentaje de espacio asignado dentro del file system del S.O. de su proyecto
     *      - Free space: 50KB. 50%
     *      - Total space: 100KB
     * @return void
    ***********************************************/
    private static void du() {
        Println("");
        int total_space = CPU.fileSystem.getTotalFreeSpace();
        int free_space = CPU.fileSystem.getFreeSpace();
        int non_free_space = CPU.fileSystem.getNonFreeSpace();
        int non_free_space_real = CPU.fileSystem.getTamanoFile(0);
        int free_space_real = total_space - non_free_space_real;
        int waste_space = non_free_space - non_free_space_real;

        NumberFormat formatter = NumberFormat.getNumberInstance();
        formatter.setMinimumFractionDigits(3);

        String format = "\t -> %1$-20s |   %2$-8s Bytes  |   %3$-5s\n";
        Println("\t-------------------------------------------------------------");
        System.out.format(format, "Free space", free_space, formatter.format(100*((double)(free_space)/(double)(total_space)))+"%");
        System.out.format(format, "Non free space", non_free_space, formatter.format(100*((double)(non_free_space)/(double)(total_space)))+"%");
        Println("\t-------------------------------------------------------------");
        System.out.format(format, "Real free space", free_space_real, formatter.format(100*((double)(free_space_real)/(double)(total_space)))+"%");
        System.out.format(format, "Real non free space", non_free_space_real, formatter.format(100*((double)(non_free_space_real)/(double)(total_space)))+"%");
        Println("\t-------------------------------------------------------------");
        System.out.format(format, "Waste space", waste_space, formatter.format(100*((double)(waste_space)/(double)(total_space)))+"%");
        System.out.format(format, "Total space", total_space, "-----");
        Println("\t-------------------------------------------------------------");

        Println("");
    }

    /***********************************************
     * Método: kill
     * Uso: Termina el proceso con pid indicado.
     * @return void
    ***********************************************/
    private static void kill(String valor) {
        //TODO verificar el kill...
        int PID_a_matar=-1;
        try{
            PID_a_matar = Integer.parseInt(valor);

            int j=-1;
            LinkedList<Proceso> temp;
            //revisar en cada cola...
            for(int a=0;a<CPU.lista_colas.size();a++){

                for(int i=0;i<CPU.lista_colas.get(a).getCola().size();i++){

                    if(CPU.lista_colas.get(a).getCola().get(i).getPID()==PID_a_matar){
                        CPU.Procesos_por_matar.add(CPU.lista_colas.get(a).getCola().get(i));
                        break;
                    }
                }

            }

        }catch(Exception exp){
            System.out.println("Parámetros de kill erróneos");
        }
        System.out.println("killing process! :P!");

    }

    /***********************************************
     * Método: ps
     * Uso: Muestra todos los procesos activos dentro
     *      del S.O. Además, debe mostrar los archivos
     *      que cada proceso tiene en uso.
     * @return void
    ***********************************************/
    private static void ps() {

        String resultado="";

        //resultado+=("Todos los procesos\n");
        LinkedList<Proceso> temp3 = CPU.Todos_Procesos;//obtiene la lista
        if(temp3.isEmpty()){
            resultado+=("Aún no hay procesos en cola\n");
        }
        for(int i=0;i<temp3.size();i++){
            resultado+=("Process: "+temp3.get(i).getPID()+", State: "+temp3.get(i).get_Nombre_Estado()+" ("+temp3.get(i).getEstado()+")")+"\n";
            resultado+="\t"+("Proceso padre: "+temp3.get(i).getPadre()+"\n");
            resultado+="\t"+("Prioridad: "+temp3.get(i).getPrioridad()+"\n");
            resultado+="\t"+("Se encuentra en cola: "+(temp3.get(i).cola_en_la_que_esta+1)+"\n");
            String temporal = "";
            
            LinkedList<Archivo> cola = CPU.Archivos_PID;
            for(int j=0;j<cola.size();j++){
                if(temp3.get(i).getPID()==cola.get(j).getPID_de_controlador()){
                    temporal+=cola.get(i).getPath();
                }
            }
            resultado+="\t"+("Archivos en uso: "+((temporal.compareTo("")==0)?"Ninguno":temporal)+"\n");
            
        }
        Print(resultado);
    }

    /***********************************************
     * Método: debug_mode
     * Uso: para activar el debug de un proceso
     * @return void
    ***********************************************/
    private static void debug_mode(String string) {
        String[] temp = string.split(" ");
        if(comparar_cadenas(temp[1],"true")){
            debug_mode = true;
            System.out.println("Debug mode: true");
        }
        else if(comparar_cadenas(temp[1],"false")){
            debug_mode = false;
            System.out.println("Debug mode: false");
        }
        else{
            System.out.println("Parámetro del debug incorrecto");
        }
    }

    /***********************************************
     * Método: rm_logs
     * Uso: borrar los logs de io.log y ps.log
     * @return void
    ***********************************************/
    private static void rm_logs() {
        File fichero = new File(path_io_log);
        File fichero2 = new File(path_ps_log);
        File fichero3 = new File(path_mem_log);
        fichero.delete();
        fichero2.delete();
        fichero3.delete();

    }

    /***********************************************
     * Método: top
     * Uso: imprime en pantalla:
     *      - contenido de frames
     *      - % de asignación
     * @return void
    ***********************************************/
    private static void top() {

        Print(CPU.RAM.toString());
        Print(CPU.SWAP.toString());
        
    }

    /***********************************************
     * Método: Crear_Proceso
     * Uso: crea un proceso con la cadena
     * @param cadena que contiene el proceso que se desea
     * @return void
    ***********************************************/
    public static void Crear_Proceso(String cadena) {

        String[] cadena_separada = cadena.split(" ");
        String nombre_archivo_destino ="";
        boolean sincrono=true;
        boolean temp_correcto = true;

        int prioridad = 4;// prioridad default
        int cant_pages = 2;

        //revisar si se le está asignando pages
        if(cadena_separada[cadena_separada.length-1].startsWith("m") || cadena_separada[cadena_separada.length-1].startsWith("M")){
            try{
                int num = Integer.parseInt(cadena_separada[cadena_separada.length-1].substring(1));
                if(num>=0){
                    cant_pages = num; //asigna pages
                }
            }catch(Exception exp){Print("valor no numérico como page");temp_correcto=false;}
            cadena = cadena.substring(0, cadena.lastIndexOf(" "));
            cadena_separada = cadena.split(" ");
        }

        //revisar si se le está asignando prioridad
        if(cadena_separada[cadena_separada.length-1].startsWith("p") || cadena_separada[cadena_separada.length-1].startsWith("P")){
            try{
                int num = Integer.parseInt(cadena_separada[cadena_separada.length-1].substring(1));
                if(num<=9 && num>=0){
                    prioridad = num; //asigna prioridad
                }
            }catch(Exception exp){Print("valor no numérico como prioridad");temp_correcto=false;}
            cadena = cadena.substring(0, cadena.lastIndexOf(" "));
            cadena_separada = cadena.split(" ");
        }
        //System.out.println("."+cadena+".");//TODO quitar esto
        //System.out.println("."+prioridad+".");//TODO quitar esto

        try{
            if(cadena.contains(">")){
                if(comparar_cadenas(cadena_separada[cadena_separada.length-2],">")){
                    sincrono = false;
                    nombre_archivo_destino = cadena_separada[cadena_separada.length-1];
                    cadena = cadena.substring(0, cadena.lastIndexOf(">")-1);
                    cadena_separada = cadena.split(" ");
                }
            }
        }catch(Exception exp){}
        //System.out.println("."+cadena+".");//TODO quitar esto
        int cantidad_registros = 16;
        if((cadena_separada.length<=(cantidad_registros+1) && cadena_separada.length>0)){

            for(int i=1;i<cadena_separada.length;i++){
                try{
                    Integer.parseInt(cadena_separada[i]);
                }catch(Exception exp){temp_correcto=false; System.out.println("parámetros incorrectos"); break;}

            }

            if(temp_correcto){
                int indice_hijo = CPU.fileSystem.getIndiceHijo(Shell.getRoot(), cadena_separada[0]);
                FCB fcb = CPU.fileSystem.getDirectorio(indice_hijo);
                    //File archivo_ejecutar = new File(Directorio_Raiz.getPath()+"/"+cadena_separada[0]);
                try {
                    
                    User_program(cadena_separada, fcb, sincrono, nombre_archivo_destino,prioridad, cant_pages);
                } catch (Exception ex) {
                    System.out.println("error al abrir el archivo: "+CPU.fileSystem.getPath(indice_hijo));
                }
            }
            
        }
        else{
            System.out.println("Cantidad de parámetros incorrecta...");
        }
    }

    /***********************************************
     * Método: User_program
     * Uso: constructor
     * @return void
    ***********************************************/
    public static void User_program(String[] cadena_separada, FCB fcb, boolean Sincrono, String nombre_archivo_destino,int prioridad, int cant_pages) {

        Proceso temp_proceso;
        if(leer_archivo(fcb)){
            if(Sincrono){
                //modo síncrono
                temp_proceso = new Proceso(lista_instrucciones,cadena_separada, fcb,prioridad, cant_pages);
            }
            else{
                //modo asíncrono
                temp_proceso = new Proceso(lista_instrucciones,cadena_separada, fcb, nombre_archivo_destino,prioridad, cant_pages);
            }
            //para revisar si agregar a la pila o no está correcta la inicialización del proceso
            if(temp_proceso.proceso_correcto()){
                //procede a agregar a la cola
                //System.out.println("si entra");
                CPU.Agregar_Proceso(temp_proceso);
            }
            else{
                System.out.println("hubo un error en la inicialización del proceso");
            }
        }
    }

    /***********************************************
     * Método: leer_archivo
     * Uso: lee un archivo indicado por el path
     * @param path del archivo a leer
     * @return booleano diciendo si se leyó bien el archivo
    ***********************************************/
    private static boolean leer_archivo(FCB fcb) {
        String sCadena ="", archivo="";

        int a = fcb.getId_value();
        if(CPU.fileSystem.getDirectorio(a).isArchivo()){
            int b = CPU.fileSystem.getDirectorio(a).getPrimer_bloque_de_datos_value();
            int c = CPU.fileSystem.getDirectorio(a).getTamano_real_value();
            archivo = (CPU.fileSystem.getAllDatos_string(b,c));
        }
        else{
            CPU.fileSystem.print_folder(a);
        }
        //Println("tamaño de bytes: "+archivo.length());
        //borrar la lista existente
        lista_instrucciones.clear();
        if(archivo.isEmpty()){
            return false;
        }
        try{
            String[] arreglo = archivo.split("\n");

            for(String z : arreglo){
                lista_instrucciones.add(z);
            }
        }catch(Exception exception){
            System.out.println("error en el archivo");
            return false;
        }
        return true;
    }

    /***********************************************
     * Método: Print
     * Uso: decide si escribir en un archivo o en la terminal
     *      dependiendo de si es síncrono o no
     * @param imprimir contiene lo que se desea imprimir
     * @return void
    ***********************************************/
    public static void Print(String imprimir) {

        //si es síncrona se imprime en la terminal
        if(sincrono){
            System.out.print(imprimir);
        }
        //si no es síncrona se imprime en el archivo
        else{
            escribir_archivo(imprimir);
        }
    }

    /***********************************************
     * Método: Println
     * Uso: decide si escribir en un archivo o en la terminal
     *      dependiendo de si es síncrono o no
     * @param imprimir contiene lo que se desea imprimir
     * @return void
    ***********************************************/
    private static void Println(String imprimir) {

        //si es síncrona se imprime en la terminal
        if(sincrono){
            System.out.print(imprimir+"\n");
        }
        //si no es síncrona se imprime en el archivo
        else{
            escribir_archivo(imprimir+"\n");
        }
    }

    /***********************************************
     * Método: escribir_archivo
     * Uso: escribe en un archivo la cadena a imprimir
     *      en un archivo descrito por el path
     * @param path del archivo a escribir
     * @param imprimir. El contenido que se imprime en el archivo
     * @return boolean diciendo si se escribió el archivo correctamente
    ***********************************************/
    private static boolean escribir_archivo(String imprimir) {

        try{
            //System.out.println("imprimir: "+imprimir);
            out.write(imprimir);
            out.close();
            sincrono=true;

        }catch(Exception exp){
            System.out.println("Error al guardar archivo\n");
            return false;
        }
        return true;

    }

    /***********************************************
     * Método: escribir_io_log
     * Uso: escribe el log con las peteciones de abrir y cerrar archivos
     * @param imprimir contiene lo que se desa imprimir
     * @return boolean diciendo si se guardó el archivo correctamente
    ***********************************************/
    private static boolean escribir_io_log(String imprimir) {

        File TextFile = new File(Shell.path_io_log);
        FileWriter TextOut;
        try {
            TextOut = new FileWriter(TextFile, true);
            TextOut.write(imprimir+"\n");
            TextOut.close();
        } catch (IOException ex) {
            System.out.println("error al guardar el log de io");
            return false;
        }
        return true;

    }

    /***********************************************
     * Método: escribir_io_log
     * Uso: escribe el log con las peteciones de abrir y cerrar archivos
     * @param imprimir contiene lo que se desa imprimir
     * @return boolean diciendo si se guardó el archivo correctamente
    ***********************************************/
    private static boolean escribir_ps_log(String imprimir) {

        File TextFile = new File(Shell.path_ps_log);
        FileWriter TextOut;
        try {
            TextOut = new FileWriter(TextFile, true);
            TextOut.write(imprimir+"\n");
            TextOut.close();
        } catch (IOException ex) {
            System.out.println("error al guardar el log de ps");
            return false;
        }
        return true;

    }

    /***********************************************
     * Método: escribir_mem_log
     * Uso: escribe el log con las peteciones de abrir y cerrar archivos
     * @param imprimir contiene lo que se desa imprimir
     * @return boolean diciendo si se guardó el archivo correctamente
    ***********************************************/
    public static boolean escribir_mem_log(String imprimir) {

        File TextFile = new File(Shell.path_mem_log);
        FileWriter TextOut;
        //Println("A imprimir en mem_log: ."+imprimir+".");
        try {
            TextOut = new FileWriter(TextFile, true);
            TextOut.write(imprimir+"\n");
            TextOut.close();
        } catch (IOException ex) {
            System.out.println("error al guardar el log de mem");
            return false;
        }
        return true;

    }

    public static int getRoot(){
        return Shell.root;
    }

}
